package hyl.core.run;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import hyl.core.MyDate;
import hyl.core.MyFun;

/*
 * 定时任务是需要连续运行的线程 所以需要判断是否超界
 * */
public class MyTimer extends AExecute {
	public static final Logger Log = LoggerFactory.getLogger(MyTimer.class);
	// 最大线程池数量
	protected static int MAXPOOLNUM = 32;
	protected static Map<String, MyTimer> servicemap = new ConcurrentHashMap<>();
	protected ScheduledExecutorService _es;
	private static AtomicInteger _executenum = new AtomicInteger(0);
	/**
	 * 实际线程数
	 */
	private AtomicInteger _threadnum = new AtomicInteger(0);
	/**
	 * 默认线程池大小
	 */
	private int _maxNum = 8;

	MyTimer(String servicename, ScheduledExecutorService es) {
		_name = servicename;
		_es = es;
		_executenum.incrementAndGet();
	}

	public static void closeAll() {
		for (String key : servicemap.keySet()) {
			MyTimer ss = servicemap.get(key);
			ss.close();
		}
		servicemap.clear();
	}

	public static void close(String servicename) {
		MyTimer ss = servicemap.get(servicename);
		if (ss == null)
			return;
		ss.close();
		servicemap.remove(servicename);
	}

	/**
	 * 适用于生产环境
	 */
	public void close() {
		close(_es);
		Log.info(_name + "定时池已关闭");
	}

	/**
	 * 适用于test环境 因为junit 的线程 是非守护线程
	 */
	public void waitAndClose() {
		try {
			waitAndClose(_es);
			Log.info(_name + "定时池已清空并关闭");
		} catch (InterruptedException e) {
			Log.info(_name + "定时池中断出现异常");
			e.printStackTrace();
		}
	}

	public void setMaxNum(int num) {
		if (num > MAXPOOLNUM || num < 1)
			_maxNum = MAXPOOLNUM;
		else
			_maxNum = num;
	}

	public static MyTimer getInstance() {
		return getInstance(null, 1);
	}

	public static MyTimer getInstance(String servicename) {
		return getInstance(servicename, 1);
	}

	public static MyTimer getInstance(String servicename, Integer maxnum) {
		if (MyFun.isEmpty(servicename)) {// servicename为空,则创建一个线程,线程名称自增
			if (_executenum.intValue() > MAXPOOLNUM) {
				Log.error("定时任务超过了最大数量");
				return null;
			} else {
				servicename = "定时" + _executenum.intValue();
			}
		}
		if (servicemap.containsKey(servicename))
			return servicemap.get(servicename);
		ScheduledExecutorService es = null;
		if (maxnum == null || MAXPOOLNUM < maxnum) {
			maxnum = MAXPOOLNUM;
			es = Executors.newScheduledThreadPool(maxnum);
		} else if (maxnum <= 1) {
			maxnum = 1;
			es = Executors.newSingleThreadScheduledExecutor();
		} else {
			es = Executors.newScheduledThreadPool(maxnum);
		}
		MyTimer mr = new MyTimer(servicename, es);
		mr.setMaxNum(maxnum);
		servicemap.put(servicename, mr);
		return mr;
	}

	private boolean checkThreadNum() {
		if (_threadnum.intValue() > _maxNum) {
			Log.error("定时任务超过了最大数量");
			return false;
		}
		_threadnum.incrementAndGet();
		return true;
	}

	// TimerTask 自定义的任务对象
	// firstTime为Date类型,从firstTime时刻开始，
	// period为long 每隔period毫秒执行一次。
	// delay 为long类型：从现在起过delay毫秒之后执行一次（不周期）
	// time为Date类型：在指定时间执行一次（不周期）。
	// 以下是几种常用调度task的方法：
	/**
	 * 定时在某个时刻执行 *
	 */
	public boolean timing(Runnable task, Date 开始时间) {
		long delay = MyFun.getMs() - 开始时间.getTime();
		return timing(task, delay, TimeUnit.MILLISECONDS);
	};

	/**
	 * 延时一段时间执行
	 * 
	 * 延时 单位: 毫秒
	 */
	public boolean timing(Runnable task, long 延时) {
		return timing(task, 延时, TimeUnit.MILLISECONDS);
	};

	/**
	 * 延时一段时间开始执行
	 */
	public boolean timing(Runnable task, long 延时, TimeUnit unit) {
		if (!checkThreadNum())
			return false;
		_es.schedule(task, 延时, unit);
		return true;
	};

	/**
	 * 间隔一段时间重复执行 period 毫秒
	 * 
	 * @param task
	 * @param firstTime
	 * @param period
	 * @return
	 */

	public boolean start(Runnable task, Date 开始时间, long 间隔毫秒) {
		long l = 开始时间.getTime() - MyFun.getMs();
		// long l = MyFun.getMs()-firstTime.getTime();
		// System.out.println(l);
		return start(task, l, 间隔毫秒, TimeUnit.MILLISECONDS);
	}

	/*
	 * public boolean start(Runnable task, Date delay, long period) { if
	 * (!checkThreadNum()) return false; // 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
	 * _es.scheduleAtFixedRate(task, delay, period); return true; }
	 */
	/**
	 * 间隔一段时间重复执行 period 毫秒
	 */
	public boolean start(Runnable task, long 间隔毫秒) {
		return start(task, 0, 间隔毫秒, TimeUnit.MILLISECONDS);
	}

	/**
	 * * 间隔一段时间重复执行
	 * 
	 * @param task
	 * @param delay
	 * @param period
	 * @return
	 */

	public boolean start(Runnable task, long 延时毫秒, long 间隔毫秒) {
		return start(task, 延时毫秒, 间隔毫秒, TimeUnit.MILLISECONDS);
	}

	/**
	 * 间隔一段时间重复执行
	 * 
	 * period 毫秒
	 */
	public boolean start(Runnable task, long 延时毫秒, long 间隔毫秒, TimeUnit unit) {
		if (!checkThreadNum())
			return false;
		// 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
		_es.scheduleAtFixedRate(task, 延时毫秒, 间隔毫秒, unit);
		return true;
	}

	/**
	 * 每月某一天执行 done : 回调函数<br>
	 * 
	 * @param daynum ：每个月的哪一号进行 0 表示最后一天 ，-1 表示倒数第二天，1 表示1号
	 */
	public boolean monStart(IDo<Calendar> done, Date 开始时间, Integer daynum) {
		if (done == null)
			return false;
		开始时间 = MyDate.getEffDateOfNext(开始时间, MyDate.MS_DAY);
		// System.out.println(MyFun.date2Str(firstTime));
		Runnable tt = new Runnable() {
			@Override
			public void run() {
				Calendar c = Calendar.getInstance();
				int day = c.get(Calendar.DAY_OF_MONTH);
				int day1 = 1;
				if (daynum == null) {
					day1 = 1;
				} else if (daynum < 1) {
					c.add(Calendar.MONTH, 1);
					c.set(Calendar.DAY_OF_MONTH, daynum);
					day1 = c.get(Calendar.DAY_OF_MONTH);
				} else {
					day1 = daynum;
				}
				// System.out.println(day1+":"+day);
				if (day1 != day)
					return;
				try {
					done.run(c);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		return start(tt, 开始时间, MyDate.MS_DAY);
	}

	/**
	 * 每日执行 done : 回调函数<br>
	 * firstTime：开始时间;<br>
	 * day ：每个月的哪一号进行 0 表示最后一天 ，-1 表示倒数第二天，1 表示1号
	 */
	public boolean dayStart(IDo<Calendar> done, Date 开始时间) {
		if (done == null)
			return false;
		开始时间 = MyDate.getEffDateOfNext(开始时间, MyDate.MS_DAY);
		Runnable tt = new Runnable() {
			@Override
			public void run() {
				Calendar c = Calendar.getInstance();
				try {
					done.run(c);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		return start(tt, 开始时间, MyDate.MS_DAY);
	}
}
